/////////////////////////////////
// Include files
#include "includes_fw.h"
#include "config_fw.h"
#include "mmp_lib.h"
#include "os_wrap.h"
#include "lib_retina.h"

#include "mmpf_pll.h"
#include "mmpf_i2cm.h"
#include "mmp_reg_gbl.h"
#include "mmp_reg_gpio.h"
#include "mmpf_system.h"
#include "mmpd_system.h"

//#define udelay(_x_)   // we don't need udelay in user mode, since there has already had delay
#define msleep(_x_) MMPF_OS_Sleep(_x_);  //delay in ms.
#define udelay(_x_) MMPF_OS_Sleep(1);    //delay in us.

#include "cxpump.h"
//#if defined( __BIG_ENDIAN) && !defined(__BYTE_ORDER)
//#define __BYTE_ORDER __BIG_ENDIAN
//#elif defined( __LITTLE_ENDIAN ) && !defined(__BYTE_ORDER)
#define __LITTLE_ENDIAN 1
#define __BYTE_ORDER __LITTLE_ENDIAN
//#endif 


#ifndef __BYTE_ORDER
#error __BYTE_ORDER is undefined.
#endif 

#define ENABLE_I2C_BURST_MODE
//#define ENABLE_EEPROM_BOOT_ROM_METHOD 

#if (SUPPORT_CNXT_FW_DOWNLOADING_EEPROM)

#define S_DESC          "Cnxt Channel Firmware"  /*Specify the string that will show on head of rom file*/
#define S_ROM_FILE_NAME "cx2070x.fw"            /*Specify the file name of rom file*/
#define CHIP_ADDR        0x14                    /*Specify the i2c chip address*/
#define MEMORY_UPDATE_TIMEOUT  300
#define MAX_ROM_SIZE (1024*1024)
const unsigned long EEPROM_WRITE_DELAY   = 20;   //10 ms

#if defined(_MSC_VER)
#define DBG_ERROR  
#define DBG_INFO   
#define LOG( _msg_ ) printf  _msg_ 
#else
#define DBG_ERROR  
#define DBG_INFO   
#define LOG( _msg_ ) RTNA_DBG_Str3 _msg_
#endif

typedef struct CX_CODEC_ROM_DATA
{
#ifdef USE_TYPE_DEFINE
    unsigned long      Type;
#endif //#ifdef USE_TYPE_DEFINE
    unsigned char      Length[4];
    unsigned char      Address[4];
    unsigned char      data[1];
}CX_CODEC_ROM_DATA;

#define ROM_DATA_TYPE_S37           0xAA55CC01 // S37 format.
#define ROM_DATA_TYPE_CNXT          0xAA55CC04 // Conexant SPI format.
#define ROM_DATA_SEPARATED_LINE     0x23232323 //()()()()

typedef struct CX_CODEC_ROM{
    char                        sDesc[24]; 
    char                        cOpenBracket;
    char                        sVersion[5];
    char                        cCloseBracket;
    char                        cEOF;
    unsigned long               FileSize;
    unsigned long               LoaderAddr;
    unsigned long               LoaderLen;
    unsigned long               CtlAddr;
    unsigned long               CtlLen;
    unsigned long               SpxAddr;
    unsigned long               SpxLen;
    struct CX_CODEC_ROM_DATA    Data[1];
}CX_CODEC_ROM;

// To convert two digital ASCII into one BYTE.
unsigned char ASCII_2_BYTE( char ah, char al) ;

#define BUF_SIZE 0x1000
#define BIBF(_x_) if(!(_x_)) break;
#define BIF(_x_) if(ErrNo=(_x_)) break;


#ifndef BIBF
#define BIBF( _x_ ) if(!(_x_)) break;
#endif 

enum { 
    MEM_TYPE_RAM     = 1 /* CTL*/, 
    MEM_TYPE_SPX     = 2,
    MEM_TYPE_EEPROM  = 3,
    MEM_TYPE_EEPROM_RESET  = 0x8003, //reset after writing.
}; 


fun_I2cWriteThenRead g_I2cWriteThenReadPtr      = NULL;
fun_I2cWrite         g_I2cWritePtr              = NULL;
unsigned char *      g_AllocatedBuffer          = NULL;
unsigned char *      g_Buffer                   = NULL;
unsigned long        g_cbMaxWriteBufSize        = 0;
void *               g_pContextI2cWrite         = NULL;
void *               g_pContextI2cWriteThenRead = NULL;


/*
 * The SetupI2cWriteCallback sets the I2cWrite callback function.
 * 
 * PARAMETERS
 *  
 *    pCallbackContext [in] - A pointer to a caller-defined structure of data items
 *                            to be passed as the context parameter of the callback
 *                            routine each time it is called. 
 *
 *    I2cWritePtr      [in] - A pointer to a i2cwirte callback routine, which is to 
 *                            write I2C data. The callback routine must conform to 
 *                            the following prototype:
 * 
 *                        int (*fun_I2cWrite)(  
 *                                void * pCallbackContext,
 *                                unsigned char ChipAddr,
 *                                unsigned long cbBuf, 
 *                                unsigned char* pBuf
 *                             );
 *
 *                        The callback routine parameters are as follows:
 *
 *                        pCallbackContext [in] - A pointer to a caller-supplied 
 *                                                context area as specified in the
 *                                                CallbackContext parameter of 
 *                                                SetupI2cWriteCallback. 
 *                        ChipAddr         [in] - The i2c chip address.
 *                        cbBuf            [in] - The size of the input buffer, in bytes.
 *                        pBuf             [in] - A pointer to the input buffer that contains 
 *                                                the data required to perform the operation.
 *
 *
 *    cbMaxWriteBuf    [in] - Specify the maximux transfer size for a I2c continue 
 *                            writing with 'STOP'. This is limited in I2C bus Master
 *                            device. The size can not less then 3 since Channel 
 *                            requires 2 address bytes plus a data byte.
 *                              
 *
 *
 * RETURN
 *  
 *    None
 *
 */
void SetupI2cWriteCallback( void * pCallbackContext,
                            fun_I2cWrite         I2cWritePtr,
                            unsigned long        cbMaxWriteBufSize)
{
    g_pContextI2cWrite  = pCallbackContext;
    g_I2cWritePtr       = I2cWritePtr;
    g_cbMaxWriteBufSize = cbMaxWriteBufSize;
}

/*
 * The SetupI2cWriteThenReadCallback sets the SetupI2cWriteThenRead callback function.
 * 
 * PARAMETERS
 *  
 *    pCallbackContext    [in] - A pointer to a caller-defined structure of data items
 *                               to be passed as the context parameter of the callback
 *                               routine each time it is called. 
 *
 *    I2cWriteThenReadPtr [in] - A pointer to a i2cwirte callback routine, which is to 
 *                               write I2C data. The callback routine must conform to 
 *                               the following prototype:
 *
 *                        int (*fun_I2cWriteThenRead)(  
 *                                void * pCallbackContext,
 *                                unsigned char ChipAddr,
 *                                unsigned long cbBuf, 
 *                                unsigned char* pBuf
 *                             );
 *
 *                        The callback routine parameters are as follows:
 *
 *                         pCallbackContext [in] - A pointer to a caller-supplied 
 *                                                 context area as specified in the
 *                                                 CallbackContext parameter of 
 *                                                 SetupI2cWriteCallback. 
 *                         ChipAddr         [in] - The i2c chip address.
 *                         cbBuf            [in] - The size of the input buffer, in bytes.
 *                         pBuf             [in] - A pointer to the input buffer that contains 
 *                                                 the data required to perform the operation.
 *
 * RETURN
 *      None
 * 
 */
void SetupI2cWriteThenReadCallback( void * pCallbackContext,
                fun_I2cWriteThenRead I2cWriteThenReadPtr)
{
    g_pContextI2cWriteThenRead  = pCallbackContext;
    g_I2cWriteThenReadPtr       = I2cWriteThenReadPtr;
}

void * AllocMem(unsigned long NumOfBytes)
{
    return (void*)mxalloc(NumOfBytes);  
}

void FreeMem(void * pMem)
{
    mxfree(pMem);
}

void SetupMemoryBuffer(void * pAllocedMemoryBuffer)
{
    g_AllocatedBuffer = (unsigned char*)pAllocedMemoryBuffer;
    if(g_AllocatedBuffer)
        g_Buffer = g_AllocatedBuffer +2;
}

/*
 * Convert a 4-byte number from a ByteOrder into another ByteOrder.
 */
unsigned long ByteOrderSwapULONG(unsigned long i)
{
    return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff);
}

/*
 * Convert a 2-byte number from a ByteOrder into another ByteOrder.
 */
unsigned short ByteOrderSwapWORD(unsigned short i)
{
    return ((i>>8)&0xff)+((i << 8)&0xff00);
}

/*
 * Convert a 4-byte number from generic byte order into Big Endia
 */
unsigned long ToBigEndiaULONG(unsigned long i)
{
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
    return ByteOrderSwapULONG(i);
#else
    return i;
#endif
}


/*
 * Convert a 2-byte number from generic byte order into Big Endia
 */
unsigned short ToBigEndiaWORD(unsigned short i)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    return ByteOrderSwapWORD(i);
#else
    return i;
#endif
}

/*
 * Convert a 4-byte number from Big Endia into generic byte order.
 */
unsigned long FromBigEndiaULONG(unsigned long i)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    return ByteOrderSwapULONG(i);
#else
    return i;
#endif
}


/*
 * Convert a 2-byte number from Big Endia into generic byte order.
 */
unsigned short FromBigEndiaWORD(unsigned short i)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    return ByteOrderSwapWORD(i);
#else
    return i;
#endif
}


/*
 * To convert two digital ASCII into one BYTE.
 */
unsigned char ASCII_2_BYTE( char ah, char al) 
{
    unsigned char ret = '\0';
    int i =2;

    for(;i>0;i--)
    {
        if( 'a' <= ah && 'f' >= ah)
        {
            ret += ah - 'a'+10;
        }
        else if( 'A' <= ah && 'F' >= ah)
        {
            ret += ah -'A'+10;
        }

        else if( '0' <= ah && '9' >= ah)
        {
            ret += ah - '0';
        }
        else
        {
            LOG((DBG_ERROR "Invalid txt data.\n"));
           
           // ErrNo = ERRNO_INVALID_DATA;
            break;
        }
        ah =al;
        if(i==2)
            ret = (unsigned short)ret << 4;
    }
    return ret;
}

/*
 * Read a byte from the specified  register address.
 * 
 * PARAMETERS
 *  
 *    RegAddr             [in] - Specifies the register address.
 *
 * RETURN
 *  
 *    Returns the byte that is read from the specified register address.
 *
 */
unsigned char CNXTReadReg(unsigned short RegAddr)
{

    unsigned char RegData;

    if(!g_I2cWriteThenReadPtr)
    {
         LOG((DBG_ERROR "i2C function is not set.\n"));
         return 0;
    }
    


    RegAddr = ToBigEndiaWORD(RegAddr);

    g_I2cWriteThenReadPtr(g_pContextI2cWriteThenRead,CHIP_ADDR,
        2,(unsigned char*) &RegAddr,1,&RegData);

    return RegData;
}


/*
 * Write a byte from the specified register address.
 * 
 * PARAMETERS
 *  
 *    RegAddr             [in] - Specifies the register address.
 *
 * RETURN
 *  
 *    Returns the byte that is read from the specified register address.
 *
 * REMARK
 * 
 *    The g_I2cWriteThenReadPtr must be set before calling this function.
 */
int CNXTWriteReg(unsigned short RegAddr, unsigned char RegData)
{
    unsigned char WrBuf[3];

    if(!g_I2cWritePtr)
    {
        LOG((DBG_ERROR "i2C function is not set.\n"));
        return ERRNO_I2CFUN_NOT_SET;
    }

    *((unsigned short*) WrBuf) = ToBigEndiaWORD(RegAddr);
    WrBuf[2] = RegData;

    g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR,sizeof(WrBuf),WrBuf);

    return ERRNO_NOERR;
}

/*
 *  Writes a number of bytes from a buffer to Channel via I2C bus.
 *  
 * PARAMETERS
 *  
 *    NumOfBytes         [in] - Specifies the number of bytes to be written
 *                              to the memory address.
 *    pData              [in] - Pointer to a buffer from an array of I2C data
 *                              are to be written.
 *  
 * RETURN
 *  
 *    If the operation completes successfully, the return value is ERRNO_NOERR.
 *    Otherwise, return ERRON_* error code. 
 */
int ChannelI2cBulkWrite( unsigned long NumOfBytes, unsigned char *pData)
{
    int ErrNo           = ERRNO_NOERR;
    unsigned short CurAddr;

    //unsigned char  *pDataEnd            = pData + NumOfBytes;
    unsigned char  *pCurData            = pData;
    unsigned char *pCurAddrByte        = NULL;
    unsigned long  BytesToProcess       = 0;
    unsigned char backup, backup1;
    const unsigned long cbAddressBytes  = 2;
    const unsigned long cbMaxDataLen    = g_cbMaxWriteBufSize-cbAddressBytes;

//		RTNA_DBG_Str3("\n\r cbMaxDataLen   :"); RTNA_DBG_Long3(cbMaxDataLen);
//		RTNA_DBG_Str3("\n\r NumOfBytes   :"); RTNA_DBG_Long3(NumOfBytes);

    if(!g_I2cWritePtr )
    {
        LOG((DBG_ERROR "i2C function is not set.\n"));
        return ERRNO_I2CFUN_NOT_SET;
    }

    //assert(NumOfBytes < 3);
    CurAddr = FromBigEndiaWORD( *((unsigned short*)pData));

    //skip first 2 bytes data (address).
    NumOfBytes -= cbAddressBytes;
    pCurData   += cbAddressBytes;

    for(;NumOfBytes;)
    {
        BytesToProcess = NumOfBytes > cbMaxDataLen? cbMaxDataLen : NumOfBytes;
	//	RTNA_DBG_Str3("\n\r BytesToProcess   :"); RTNA_DBG_Long3(BytesToProcess);
        NumOfBytes-= BytesToProcess;
	//	RTNA_DBG_Str3("\n\r NumOfBytes   :"); RTNA_DBG_Long3(NumOfBytes);
        // save the pervious 2 bytes for later use.
        pCurAddrByte = (unsigned char*) (pCurData - cbAddressBytes);
        backup       = *pCurAddrByte ;
		backup1       = *(pCurAddrByte+1);
        *pCurAddrByte =  ToBigEndiaWORD(CurAddr) & 0xFF ;
        *(pCurAddrByte+1)=  ToBigEndiaWORD(CurAddr) >> 8 ;
	//	RTNA_DBG_Str3("\n\r Length   :"); RTNA_DBG_Byte3(BytesToProcess + cbAddressBytes);
        BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, BytesToProcess + cbAddressBytes,(unsigned char*)pCurAddrByte));
        //restore the data 
        *pCurAddrByte = backup;
        *(pCurAddrByte+1) =backup1;

        pCurData += BytesToProcess;
        CurAddr  += (unsigned short)BytesToProcess;
    }
    return ErrNo;
}


/*
 *  Writes a number of bytes from a buffer to the specified memory address.
 *
 * PARAMETERS
 *
 *    dwAddr             [in] - Specifies the memory address.
 *    NumOfBytes         [in] - Specifies the number of bytes to be written
 *                              to the memory address.
 *    pData              [in] - Pointer to a buffer from an struct of 
 *                              CX_CODEC_ROM_DATA is to be written.
 *    MemType            [in] - Specifies the requested memory type, the value must be from 
 *                              the following table.
 *
 *                              MEM_TYPE_RAM     = 1
 *                              MEM_TYPE_SPX     = 2
 *                              MEM_TYPE_EEPROM  = 3
 *
 * RETURN
 *  
 *    If the operation completes successfully, the return value is ERRNO_NOERR.
 *    Otherwise, return ERRON_* error code. 
 */
int CxWriteMemory(unsigned long dwAddr, unsigned long NumOfBytes, const unsigned char * pData, int MemType )
{
    int ErrNo           = ERRNO_NOERR;
    unsigned char      Address[4];
    unsigned char      WrData[8];
    unsigned char      offset = 0;
    const unsigned long MAX_BUF_LEN = 0x100;
    unsigned char      cr = 0;
    int                 bNeedToContinue = 0;
    int                 i=0;


    const unsigned long cbAddressBytes  = 2;

    unsigned short *    pAddressByte;
    const unsigned char       *pEndData  = pData + NumOfBytes;
    unsigned short      RegMemMapAddr = ToBigEndiaWORD(0x300);
    unsigned long       BytesToProcess = 0;

    while(NumOfBytes)
    {

        BytesToProcess = NumOfBytes <= MAX_BUF_LEN ? NumOfBytes : MAX_BUF_LEN;
        NumOfBytes -= BytesToProcess;
        pEndData  = pData + BytesToProcess;

        *((unsigned long*)&Address) = ToBigEndiaULONG(dwAddr);
        //        dwAddr += offset;
        offset = 0;

        if( !bNeedToContinue )
        {
#ifdef ENABLE_I2C_BURST_MODE
            //
            //  Update the memory target address and buffer length.
            //
            WrData[0] = 0x02;    //update target address Low 0x02FC 
            WrData[1] = 0xFC;
            WrData[2] = Address[3];
            WrData[3] = Address[2];
            WrData[4] = Address[1];
            WrData[5] = (unsigned char)BytesToProcess -1 ;  // X bytes - 1
            memcpy(g_Buffer, WrData, 6);
            ChannelI2cBulkWrite(6, (unsigned char*)g_Buffer);
#else
            //
            //  Update the memory target address and buffer length.
            //
            WrData[0] = 0x02;    //update target address Low 0x02FC 
            WrData[1] = 0xFC;
            WrData[2] = Address[3];
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

            WrData[0] = 0x02;    //update target address Middle 0x02FD
            WrData[1] = 0xFD;
            WrData[2] = Address[2];
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

            WrData[0] = 0x02;    //update target address High 0x02FE
            WrData[1] = 0xFE;
            WrData[2] = Address[1];
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

            WrData[0] = 0x02;    //update Buffer Length.  0x02FF
            WrData[1] = 0xFF;
            WrData[2] = (unsigned char)BytesToProcess -1 ;  // X bytes - 1
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));
#endif
        }

        //
        //  Update buffer.
        //
#ifdef ENABLE_I2C_BURST_MODE
        pAddressByte = (unsigned short*) (pData - cbAddressBytes);
        memcpy(g_Buffer, pAddressByte, BytesToProcess+cbAddressBytes);
        *((unsigned short*)g_Buffer) = RegMemMapAddr;
        ChannelI2cBulkWrite(BytesToProcess+cbAddressBytes, (unsigned char*)g_Buffer);
        pData = pEndData;
#else
        for(offset=0;pData != pEndData;offset++,pData++)
        {
            WrData[0] = 0x03;   //update Buffer [0x0300 - 0x03ff]
            WrData[1] = (unsigned char) offset;
            WrData[2] = *pData;
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));
        }
#endif 

        //
        // Commit the changes and start to transfer buffer to memory.
        //
        if( MemType == MEM_TYPE_RAM)
        {
            cr = 0x81;
        }
        else if( MemType  == MEM_TYPE_EEPROM)
        {
            cr = 0x83;
        }
        else if( MemType== MEM_TYPE_SPX)
        {
            cr = 0x85;
            if( bNeedToContinue )
            {
                cr |= 0x08;
            }
        }

        WrData[0] = 0x04;   // UpdateCtl [0x400]
        WrData[1] = 0x00;
        WrData[2] = cr;   // start to transfer  
        BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

        for(i = 0;i<MEMORY_UPDATE_TIMEOUT;i++)
        {

            // loop until the writing is done.
            WrData[0] = CNXTReadReg(0x0400);
            if(!( WrData[0] & 0x80 ))
            {
                //done
                break;
            }
            else
            {
                //pending
                if(MemType== MEM_TYPE_EEPROM)
                {   
                    //it needs more time for updating eeprom.
                    msleep(50);
                }
                else
                {
                    udelay(1);
                }
                continue;
            }
        }

        if( i == MEMORY_UPDATE_TIMEOUT)
        {
            //writing failed.
            LOG( (DBG_ERROR "memory update timeout.\n"));
            ErrNo = ERRNO_UPDATE_MEMORY_FAILED;

            break;
        }
        if ( i >= 1) 
        {
            //printk( KERN_ERR "write pending loop =%d\n", i);
        }	

        bNeedToContinue = 1; 
    }while(0);

    return ErrNo ;
}

 
/*
 *  Reads a number of bytes from the specified memory address.
 *
 * PARAMETERS
 *
 *    dwAddr             [in] - Specifies the memory address.
 *    NumOfBytes         [in] - Specifies the number of bytes to be written
 *                              to the memory address.
 *    pData              [in] - Pointer to a buffer from an struct of 
 *                              CX_CODEC_ROM_DATA is to be written.
 *    MemType            [in] - Specifies the requested memory type, the value must be from 
 *                              the following table.
 *
 *                              MEM_TYPE_RAM     = 1
 *                              MEM_TYPE_SPX     = 2
 *                              MEM_TYPE_EEPROM  = 3
 *
 * RETURN
 *  
 *    If the operation completes successfully, the return value is ERRNO_NOERR.
 *    Otherwise, return ERRON_* error code. 
 */
int CxReadMemory(unsigned long dwAddr, unsigned long NumOfBytes, unsigned char * pData, int MemType )
{
    int ErrNo           = ERRNO_NOERR;
    unsigned char      Address[4];
    unsigned char      WrData[8];
    unsigned char      offset = 0;
    const unsigned long MAX_BUF_LEN = 0x100;
    unsigned char      cr = 0;
    int                 bNeedToContinue = 0;
    int                 i=0;

    const unsigned long cbAddressBytes  = 2;

    unsigned short *    pAddressByte;
    unsigned char *pEndData  = pData + NumOfBytes;
    unsigned short      RegMemMapAddr = ToBigEndiaWORD(0x300);
    unsigned long       BytesToProcess = 0;

    while(NumOfBytes)
    {

        BytesToProcess = NumOfBytes <= MAX_BUF_LEN ? NumOfBytes : MAX_BUF_LEN;
        NumOfBytes -= BytesToProcess;
        pEndData  = pData + BytesToProcess;

        *((unsigned long*)&Address) = ToBigEndiaULONG(dwAddr);
        //        dwAddr += offset;
        offset = 0;

        if( !bNeedToContinue )
        {
#ifdef ENABLE_I2C_BURST_MODE
            //
            //  Update the memory target address and buffer length.
            //
            WrData[0] = 0x02;    //update target address Low 0x02FC 
            WrData[1] = 0xFC;
            WrData[2] = Address[3];
            WrData[3] = Address[2];
            WrData[4] = Address[1];
            WrData[5] = (unsigned char)BytesToProcess -1 ;  // X bytes - 1
            memcpy(g_Buffer, WrData, 6);
            ChannelI2cBulkWrite(6, (unsigned char*)g_Buffer);
#else
            //
            //  Update the memory target address and buffer length.
            //
            WrData[0] = 0x02;    //update target address Low 0x02FC 
            WrData[1] = 0xFC;
            WrData[2] = Address[3];
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

            WrData[0] = 0x02;    //update target address Middle 0x02FD
            WrData[1] = 0xFD;
            WrData[2] = Address[2];
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

            WrData[0] = 0x02;    //update target address High 0x02FE
            WrData[1] = 0xFE;
            WrData[2] = Address[1];
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

            WrData[0] = 0x02;    //update Buffer Length.  0x02FF
            WrData[1] = 0xFF;
            WrData[2] = (unsigned char)BytesToProcess -1 ;  // X bytes - 1
            BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));
#endif
        }


        //
        // Commit the changes and start to transfer buffer to memory.
        //
        if( MemType == MEM_TYPE_RAM)
        {
            cr = 0x80;
        }
        else if( MemType  == MEM_TYPE_EEPROM)
        {
            cr = 0x82;
        }
        else if( MemType== MEM_TYPE_SPX)
        {
            cr = 0x84;
            if( bNeedToContinue )
            {
                cr |= 0x08;
            }
        }

        WrData[0] = 0x04;   // UpdateCtl [0x400]
        WrData[1] = 0x00;
        WrData[2] = cr;   // start to transfer  
        BIBF(g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR, 3 , WrData));

        for(i = 0;i<MEMORY_UPDATE_TIMEOUT;i++)
        {

            // loop until the writing is done.
            WrData[0] = CNXTReadReg(0x0400);
            if(!( WrData[0] & 0x80 ))
            {
                //done
                break;
            }
            else
            {
                //pending
                if(MemType== MEM_TYPE_EEPROM)
                {   
                    //it needs more time for updating eeprom.
                    msleep(50);
                }
                else
                {
                    udelay(1);
                }
                continue;
            }
        }

        if( i == MEMORY_UPDATE_TIMEOUT)
        {
            //writing failed.
            LOG( (DBG_ERROR "memory update timeout.\n"));
            ErrNo = ERRNO_UPDATE_MEMORY_FAILED;

            break;
        }

        //
        //  Update buffer.
        //
#ifdef ENABLE_I2C_BURST_READ
        WrData[0] = 0x03;   // UpdateCtl [0x400]
        WrData[1] = 0x00;
        BIBF(g_I2cWriteThenReadPtr(g_pContextI2cWriteThenRead,CHIP_ADDR,2,WrData,BytesToProcess,pData));
        pData = pEndData;
#else
        for(offset=0;pData != pEndData;offset++,pData++)
        {
            WrData[0] = 0x03;   //update Buffer [0x0300 - 0x03ff]
            WrData[1] = (unsigned char) offset;
            g_I2cWriteThenReadPtr(g_pContextI2cWriteThenRead,CHIP_ADDR,2,WrData,1,pData);
        }
#endif 
        bNeedToContinue = 1; 
    }while(0);

    return ErrNo ;
}
#if defined(_MSC_VER)
#define  ShowProgress(_x_,_y_,_state_) LOG((DBG_INFO "Updating %05X\n",_x_));
#else 
#define  ShowProgress(_x_,_y_,_state_) {RTNA_DBG_Str3("Updating ");RTNA_DBG_Short3((_x_));RTNA_DBG_Str3("\n\r")}
#endif
int DoWriteEEPROM(const unsigned char *const pEepromData, unsigned long cbEepromData,unsigned long cbEepromPage)
{
    const long      MAX_RETRY = 4;
    unsigned long   cbFileSize = 0;  
    int             ErrNo = ERRNO_NOERR;
    
//    const unsigned long EEPROM_PAGE_SIZE = 64; // ST M24M01 :128, ATMEL : 256 bytes.
    const unsigned char *pData    = pEepromData;
    const unsigned char *pDataEnd = pData + cbEepromData;
    
    const unsigned char *    pWrite ;
    unsigned char *    pRead ;
    const unsigned char *    pWriteEnd;
    unsigned char       FirstByte = 0x4c;  //yidongq

    int i=0,j=0;
    int Retry = 0;

    unsigned char *pReadBuf = (unsigned char *)AllocMem(cbEepromPage);

    ShowProgress(0,1,I2C_OK);

    for(; pData != pDataEnd; )
    {
        BIF(CxWriteMemory(i,cbEepromPage, pData, MEM_TYPE_EEPROM));

        msleep(EEPROM_WRITE_DELAY);

       if( Retry )
        {
            msleep(EEPROM_WRITE_DELAY*(Retry+1));
        }

        // reset read buf.
        pWrite = pData;
        pRead = pReadBuf;
        pWriteEnd = pWrite + cbEepromPage;
        for( ;pWrite != pWriteEnd;pWrite++,pRead++)
        {
            *pRead = ~*pWrite;
        }

        j = i + cbEepromPage /2;
        BIF(CxReadMemory(j,cbEepromPage/2, pReadBuf + (cbEepromPage/2), MEM_TYPE_EEPROM ));
        BIF(CxReadMemory(i,cbEepromPage/2, pReadBuf, MEM_TYPE_EEPROM ));

        if(0!=memcmp(pData,pReadBuf,cbEepromPage))
        {
            if( Retry < MAX_RETRY )
            {
                ShowProgress(i,FALSE,I2C_RETRY);	
                Retry++;
                if( Retry == 0) { msleep(500);}

                continue;
            }
            else
            {
                ShowProgress(i,FALSE,I2C_ERR);	
                ErrNo = ERRON_UPDATE_EEPROM_FAILED;
                LOG((DBG_ERROR "Failed to update EEPROM."));
                break;
            }
        }
        else
        {
            ShowProgress(i,FALSE,I2C_OK);
            Retry = 0;  
        }
        pData += cbEepromPage;
        i+=cbEepromPage;
    }

    FreeMem(pReadBuf);

    if(ErrNo==0)  //yidongq
    {
        //Write the first byte to 0x4c if all data have been successfully written.
       CxWriteMemory(0,1, &FirstByte, MEM_TYPE_EEPROM );
	//	BIF(CxWriteMemory(0,cbEepromPage, pData, MEM_TYPE_EEPROM));
    }
	
    return ErrNo;
}



#define WAIT_UNTIL_DEVICE_READY(_x_) \
for (timeout=0;timeout<dev_ready_time_out;timeout++) \
{                                                    \
    Ready = CNXTReadReg(0x1000);                         \
    if (Ready == (_x_)) break;                       \
    msleep(10);                                      \
};                                                   \
if( timeout == dev_ready_time_out)                   \
{                                                    \
    LOG((DBG_ERROR "cx2070x device is out of control.!! Please turn off power and try it again.")); \
    ErrNo = ERRNO_DEVICE_OUT_OF_CONTROL;             \
    break;                                           \
}            



/*
 * Download Firmware to Channel.
 * 
 * PARAMETERS
 *  
 *    pRomData            [in] - A pointer fo the input buffer that contains rom data.
 *    bWriteToEeprom      [in] - Indicates whether the firmware is downloaded to EEPROM.
 *                               If 0, download fw to ram. otherwise download fw to eeprom.
 *    pEepromData         [in] - The EEPROM context to be downloaded. 
 *                               This parameter has no effect if bWriteToEprom is zero.
 *    cbEepromData        [in] - The number of bytes to be written to the EEPROM.
 *                               This parameter has no effect if bWriteToEprom is zero.
 *    cbEepromPage        [in] - Specifies EEPROM page size. For example, the page size of
 *                               ST M24M01 is 128 bytes. ATMEL is 256 bytes.
 *
 *   void SetupMemoryBuffer(void * pAllocedMemoryBuffer);
 * RETURN
 *  
 *    If the operation completes successfully, the return value is ERRNO_NOERR.
 *    Otherwise, return ERRON_* error code. 
 * 
 * REMARKS
 *  
 *    You need to set up both I2cWrite and I2cWriteThenRead callback function by calling 
 *    SetupI2cWriteCallback and SetupI2cWriteThenReadCallback before you call this function.
 */
int DownloadFW(const unsigned char * const pRomBin,
                int bWriteToEEPROM ,
                unsigned char * const pEepromData,  //yidongq const
                unsigned long cbEepromData,
                unsigned int cbEepromPage)
{
    int ErrNo = ERRNO_NOERR;
    struct CX_CODEC_ROM      *pRom  = (struct CX_CODEC_ROM  *)pRomBin;
    struct CX_CODEC_ROM_DATA *pRomData;
    struct CX_CODEC_ROM_DATA *pRomDataEnd;
    unsigned char            *pData;
    unsigned char            *pDataEnd;
    unsigned long            CurAddr = 0;
    unsigned long            cbDataLen = 0;
    unsigned char            Ready;
    unsigned long            curProgress = 0;
    const unsigned long      dev_ready_time_out = 10;
    unsigned long            timeout=0,tmp_length=0, temp_Addr;
    unsigned char            buf[4];
    int                      bNeedBootCode = 1;

  //  unsigned char            *pDLBuf= (unsigned char*)AllocMem(0x200);
    unsigned char            *pDLBuf= (unsigned char*)mxalloc(0x200); 
    SetupMemoryBuffer(pDLBuf);

    do{
        if(pRom == NULL ||g_Buffer == NULL)
        {
            ErrNo = ERRNO_INVALID_PARAMETER;
            LOG((DBG_ERROR "Invalid parameter.\n"));
            break;
        }

        if( NULL == g_I2cWriteThenReadPtr||
            NULL == g_I2cWritePtr)
        {
            ErrNo = ERRNO_I2CFUN_NOT_SET;
            LOG((DBG_ERROR "i2C function is not set.\n"));
            break;
        }



        //check if there is a FIRMWARE present.
        if(bWriteToEEPROM == 2)
        {
            bNeedBootCode =0;
        }

        else if(( CNXTReadReg(0x009) & 0x04) == 0)
        {
	        RTNA_DBG_Str3("Firmware is present.  \n");
            // Firmware is present. 
            if( bWriteToEEPROM )
            {
                // For safty, we alwasy using boot ROM mode. so we have to do something,
                // make it return to boot ROM mode.


                bNeedBootCode = 0; //boot code is already running, we don't need re-download it again.

            }
            else
            {
                // Suppose, there is no EEPROM on system in this scenario. if firmware is presnet, 
                // it means the channel doesn't get a clear reset signal.
                LOG((DBG_ERROR  "Channel did not get a clear reset.."));
                ErrNo = ERRNO_DEVICE_NOT_RESET;
                //return ErrNo;
            }
        }

        
        if(bNeedBootCode)
        {
            //
            // Download Boot Loader.
            //
            RTNA_DBG_Str3("Download Boot Loader. \n");
			
            pRomData    = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->LoaderAddr));
            pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->LoaderLen));
RTNA_DBG_Str3("\n\r pRom->sDesc[0]  :"); RTNA_DBG_Byte3(pRom->sDesc[0]);
RTNA_DBG_Str3("\n\r pRom->cOpenBracket   :"); RTNA_DBG_Byte3(pRom->cOpenBracket);
RTNA_DBG_Str3("\n\r pRom->sVersion[0]   :"); RTNA_DBG_Byte3(pRom->sVersion[0]);
RTNA_DBG_Str3("\n\r pRom->cCloseBracket   :"); RTNA_DBG_Byte3(pRom->cCloseBracket);
RTNA_DBG_Str3("\n\r pRom->cEOF   :"); RTNA_DBG_Byte3(pRom->cEOF);
RTNA_DBG_Str3("\n\r FromBigEndiaULONG(pRom->FileSize)   :"); RTNA_DBG_Long3(FromBigEndiaULONG(pRom->FileSize));
RTNA_DBG_Str3("\n\r FromBigEndiaULONG(pRom->LoaderAddr)   :"); RTNA_DBG_Long3(FromBigEndiaULONG(pRom->LoaderAddr));
RTNA_DBG_Str3("\n\r FromBigEndiaULONG(pRom->LoaderLen)   :"); RTNA_DBG_Long3(FromBigEndiaULONG(pRom->LoaderLen));
RTNA_DBG_Str3("\n\r pRomData   :"); RTNA_DBG_Long3((MMP_ULONG)pRomData);
RTNA_DBG_Str3("\n\r pRomDataEnd   :"); RTNA_DBG_Long3((MMP_ULONG)pRomDataEnd);

            for( ;pRomData!=pRomDataEnd;)
            {
#ifdef ENABLE_I2C_BURST_MODE
                pData   = &pRomData->data[0];
				//RTNA_DBG_Str3("\n\r pRomData->Length   :"); RTNA_DBG_Long3(pRomData->Length);
				tmp_length = FromBigEndiaULONG( (pRomData->Length[0] ) +  (pRomData->Length[1]<<8 )+(pRomData->Length[2]<<16 )+(pRomData->Length[3] << 24) );
				temp_Addr = FromBigEndiaULONG((pRomData->Address[0] ) +  (pRomData->Address[1]<<8 )+(pRomData->Address[2]<<16 )+(pRomData->Address[3] ) <<24 );
				RTNA_DBG_Str3("\n\r FromBigEndiaULONG(pRomData->Length):"); RTNA_DBG_Long3(tmp_length);
				
                pDataEnd= pData + tmp_length - sizeof(unsigned long); 
                memcpy(g_Buffer, pData-2, tmp_length - sizeof(unsigned short));
                BIF(ChannelI2cBulkWrite( tmp_length - sizeof(unsigned short), g_Buffer));
                pRomData = (struct CX_CODEC_ROM_DATA *)pDataEnd;
				RTNA_DBG_Str3("\n\r pRomData offset :"); RTNA_DBG_Long3((MMP_ULONG)pRomData- (MMP_ULONG)((char*)pRom + FromBigEndiaULONG(pRom->LoaderAddr)) );
				//RTNA_DBG_Str3("\n\r pRomDataEnd:"); RTNA_DBG_Long3((MMP_ULONG)pRomDataEnd);
#else
                CurAddr = temp_Addr ;//FromBigEndiaULONG(pRomData->Address);
                pData   = &pRomData->data[0];
                pDataEnd= pData + tmp_length - sizeof(unsigned long); 
                for( ;pData!=pDataEnd;pData++)
                {
                    *((unsigned short*)buf) = ToBigEndiaWORD((unsigned short)CurAddr);
                    buf[2]= *pData;
                    g_I2cWritePtr(g_pContextI2cWrite,CHIP_ADDR,3, buf);
                    CurAddr++;
                }
                pRomData = (struct CX_CODEC_ROM_DATA *)pData;
#endif 
            }
        }

        //* check if the device is ready.
        //WAIT_UNTIL_DEVICE_READY(0x01);

        if( bWriteToEEPROM )
        {
        RTNA_DBG_Str3("bWriteToEEPROM \n");
            pEepromData[0] = 0x00;  //yidongq

            ErrNo = DoWriteEEPROM(pEepromData,cbEepromData,cbEepromPage);
			RTNA_DBG_Str3("after DoWriteEEPROM\n");
            break;
        }
        //
        // Download CTL code.
        //
         RTNA_DBG_Str3("Download CTL code. \n");

        pRomData    = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->CtlAddr ));
        pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->CtlLen));

        for( ;pRomData!=pRomDataEnd;)
        {
        cbDataLen = FromBigEndiaULONG( (pRomData->Length[0] ) +  (pRomData->Length[1]<<8 )+(pRomData->Length[2]<<16 )+(pRomData->Length[3] << 24) );
		CurAddr = FromBigEndiaULONG((pRomData->Address[0] ) +  (pRomData->Address[1]<<8 )+(pRomData->Address[2]<<16 )+(pRomData->Address[3] ) <<24 );

				
          //  CurAddr = FromBigEndiaULONG(pRomData->Address);
            pData       = &pRomData->data[0];
         //   cbDataLen   = FromBigEndiaULONG(pRomData->Length) ;
            BIF(CxWriteMemory(CurAddr,cbDataLen -sizeof(unsigned long)/*subtracts the address bytes*/ , pData, MEM_TYPE_RAM ));
            // The next RoMData position = current romData position + cbDataLen + sizeof( data len bytes)
            pRomData  =   (struct CX_CODEC_ROM_DATA *)((char*) pRomData + cbDataLen + sizeof(unsigned long));  
            curProgress = (char*)pRomDataEnd - (char*) pRomData ;
        }

        //
        // Download SPX code.
        //
        RTNA_DBG_Str3("Download SPX code. \n");
        pRomData    = (struct CX_CODEC_ROM_DATA *) ( (char*)pRom + FromBigEndiaULONG(pRom->SpxAddr ));
        pRomDataEnd = (struct CX_CODEC_ROM_DATA *) ((char*)pRomData +FromBigEndiaULONG(pRom->SpxLen));
        for( ;pRomData!=pRomDataEnd;)
        {
         cbDataLen = FromBigEndiaULONG( (pRomData->Length[0] ) +  (pRomData->Length[1]<<8 )+(pRomData->Length[2]<<16 )+(pRomData->Length[3] << 24) );
		CurAddr = FromBigEndiaULONG((pRomData->Address[0] ) +  (pRomData->Address[1]<<8 )+(pRomData->Address[2]<<16 )+(pRomData->Address[3] ) <<24 );

//            CurAddr = FromBigEndiaULONG(pRomData->Address);
            pData       = &pRomData->data[0];
  //          cbDataLen   = FromBigEndiaULONG(pRomData->Length) ;
            BIF(CxWriteMemory(CurAddr,cbDataLen -sizeof(unsigned long)/*subtracts the address bytes*/ , pData, MEM_TYPE_SPX ));
            // The next RoMData position = current romData position + cbDataLen + sizeof( data len bytes)
            pRomData  =   (struct CX_CODEC_ROM_DATA *)((char*) pRomData + cbDataLen + sizeof(unsigned long));  

            curProgress = (char*)pRomDataEnd - (char*) pRomData ;
        }

    }while(0);

    if(!ErrNo)
    {
        //
        // Software Reset
        //
        if(bWriteToEEPROM)
        {
            // boot from EEPROM.
            buf[0] = 0x42;
            return 0;
            //break;
        }
		else
        {
            // boot from ram.
            buf[0] = 0x40;
            CNXTWriteReg(0x400,buf[0]);
            msleep(100);
            do{ WAIT_UNTIL_DEVICE_READY(0x01);}while(0);
        }
    }

    FreeMem(pDLBuf);
    return ErrNo;
}
#endif

